/*
 * CS Photo Selector
 * @author: Carson Shold (@cshold)
*/
var CSPhotoSelector = (function (module, $jq) {

    // Public functions
    var init, setAlbums, getAlbums, getAlbumById, getPhotoById, setPhotos, newInstance,

    // Private variables
	settings, albums, prev, photos = new Array(),
	$jqalbums, $jqphotos, $jqcontainer, $jqalbumsContainer, $jqphotosContainer, $jqselectedCount, $jqselectedCountMax, $jqpageNumber, $jqpageNumberTotal, $jqpagePrev, $jqpageNext, $jqbuttonClose, $jqbuttonOK, $jqbuttonCancel,

    // Private functions
	$jqgetAlbumById, $jqgetPhotoById, buildAlbumSelector, buildPhotoSelector, sortPhotos, log;

    /////////////////////////////////////////
    // PUBLIC FUNCTIONS FOR GLOBAL PLUGIN
    // They are public because they are added to module and returned
    /////////////////////////////////////////

    /**
    * Initialise the plugin and define global options
    */
    init = function (options) {

        // Default settings
        settings = {
            speed: 100,
            debug: false,
            disabledClass: 'CSPhotoSelector_disabled',
            albumSelectedClass: 'CSPhotoSelector_photoSelected',
            albumDisabledClass: 'CSPhotoSelector_photoDisabled',
            photoFilteredClass: 'CSPhotoSelector_photoFiltered',
            containerSelector: '#CSPhotoSelector',
            containerSelectorLoading: '#CSPhotoSelectorLoading',
            containerSelectorNoPhotos: '#CSPhotoSelectorNoPhotos',
            albumsContainerSelector: '.CSAlbum_container',
            photosContainerSelector: '.CSPhoto_container',
            photosWrapperSelector: '.CSPhotoSelector_wrapper',
            selectedPhotoCountSelector: '.CSPhotoSelector_selectedPhotoCount',
            selectedPhotoCountMaxSelector: '.CSPhotoSelector_selectedPhotoCountMax',
            pageNumberSelector: '#CSPhotoSelector_pageNumber',
            pageNumberTotalSelector: '#CSPhotoSelector_pageNumberTotal',
            pagePrevSelector: '#CSPhotoSelector_pagePrev',
            pageNextSelector: '#CSPhotoSelector_pageNext',
            buttonBackToAlbumsSelector: '#CSPhotoSelector_backToAlbums',
            buttonCloseSelector: '#CSPhotoSelector_buttonClose',
            buttonOKSelector: '#CSPhotoSelector_buttonOK',
            buttonCancelSelector: '#CSPhotoSelector_buttonCancel',
            loader: '#CSPhotoSelector_loader',
            pagination: '.CSPhotoSelector_pageNumberContainer, #CSPhotoSelector_pagePrev, #CSPhotoSelector_pageNext'
        };

        // Override defaults with arguments
        $jq.extend(settings, options);

        // Select DOM elements
        $jqcontainer = $jq(settings.containerSelector);
        $jqcontainerLoading = $jq(settings.containerSelectorLoading);
        $jqcontainerNoPhotos = $jq(settings.containerSelectorNoPhotos);
        $jqalbumsContainer = $jqcontainer.find(settings.albumsContainerSelector);
        $jqphotosContainer = $jqcontainer.find(settings.photosContainerSelector);
        $jqphotosWrapper = $jqcontainer.find(settings.photosWrapperSelector);
        $jqselectedCount = $jqcontainer.find(settings.selectedPhotoCountSelector);
        $jqselectedCountMax = $jqcontainer.find(settings.selectedPhotoCountMaxSelector);
        $jqpageNumber = $jqcontainer.find(settings.pageNumberSelector);
        $jqpageNumberTotal = $jqcontainer.find(settings.pageNumberTotalSelector);
        $jqpagePrev = $jqcontainer.find(settings.pagePrevSelector);
        $jqpageNext = $jqcontainer.find(settings.pageNextSelector);
        $jqbackToAlbums = $jqcontainer.find(settings.buttonBackToAlbumsSelector);
        $jqbuttonClose = $jqcontainer.find(settings.buttonCloseSelector);
        $jqbuttonOK = $jqcontainer.find(settings.buttonOKSelector);
        $jqbuttonCancel = $jqcontainer.find(settings.buttonCancelSelector);
        $jqloader = $jqcontainer.find(settings.loader);
        $jqpagination = $jqcontainer.find(settings.pagination);
    };

    /**
    * If your website has already loaded the user's Facebook photos, pass them in here to avoid another API call.
    */
    setAlbums = function (input) {
        var i, len;
        if (!input || input.length === 0) {
            return;
        }
        input = Array.prototype.slice.call(input);
        input = input.sort(sortPhotos);
        albums = input;
    };

    getAlbums = function () {
        return albums;
    };

    setPhotos = function (input) {
        var i, len;
        if (!input || input.length === 0) {
            return;
        }
        input = Array.prototype.slice.call(input);
        photos = input;
    };

    getPhotos = function () {
        return photos;
    };

    /**
    * Use this function if you have a photo ID and need to know their name
    */
    getAlbumById = function (id) {
        var i, len;
        id = id.toString();
        for (i = 0, len = albums.length; i < len; i += 1) {
            if (albums[i].id === id) {
                return albums[i];
            }
        }
        return null;
    };

    getPhotoById = function (id) {
        var i, len;
        id = id.toString();
        for (i = 0, len = photos.length; i < len; i += 1) {
            if (photos[i].id === id) {
                return photos[i];
            }
        }
        return null;
    };

    /**
    * Create a new instance of the photo selector
    * @param options An object containing settings that are relevant to this particular instance
    */
    newInstance = function (options) {
        // Public functions
        var showAlbumSelector, showPhotoSelector, hidePhotoSelector, hideAlbumSelector, getselectedAlbumIds, getselectedPhotoIds, setDisabledPhotoIds, reset,

        // Private variables
		instanceSettings, selectedAlbumIds = [], selectedPhotoIds = [], disabledPhotoIds = [],

        // Private functions
		bindEvents, unbindEvents, updateAlbumContainer, updatePhotosContainer, updatePaginationButtons, selectAlbum, selectPhotos;

        if (!settings) {
            log('Cannot create a new instance of CSPhotoSelector because the plugin not initialised.');
            return false;
        }

        // Default settings
        instanceSettings = {
            maxSelection: 1,
            albumsPerPage: 6,
            photosPerPage: 10,
            autoDeselection: false, // Allow the user to keep on selecting once they reach maxSelection, and just deselect the first selected photo
            callbackAlbumSelected: null,
            callbackAlbumUnselected: null,
            callbackPhotoSelected: null,
            callbackPhotoUnselected: null,
            callbackMaxSelection: null,
            callbackSubmit: null
        };

        // Override defaults with arguments
        $jq.extend(instanceSettings, options);

        /////////////////////////////////////////
        // PUBLIC FUNCTIONS FOR AN INSTANCE
        /////////////////////////////////////////

        /**
        * Call this function to show the interface
        */
        showAlbumSelector = function (id, callback) {
            var i, len;
            log('CSPhotoSelector - show Albums');
            if (!$jqalbums) {
                return buildAlbumSelector(id, function () {
                    showAlbumSelector(id, callback);
                });
            } else {
                bindEvents();
                // Update classnames to represent the selections for this instance
                $jqalbums.removeClass(settings.albumSelectedClass + ' ' + settings.albumDisabledClass + ' ' + settings.photoFilteredClass);
                for (i = 0, len = albums.length; i < len; i += 1) {
                    if ($jq.inArray(albums[i].id, selectedAlbumIds) !== -1) {
                        $jq($jqalbums[i]).addClass(settings.albumSelectedClass);
                    }
                    if ($jq.inArray(albums[i].id, disabledPhotoIds) !== -1) {
                        $jq($jqalbums[i]).addClass(settings.albumDisabledClass);
                    }
                }
                // Update paging
               updateAlbumContainer(1);
               updatePaginationButtons(1);
               //$jqcontainer.fadeIn(100);
               $jqcontainer.css('display','block');
                if (typeof callback === 'function') {
                    callback();
                }
            }
        };

        showPhotoSelector = function (callback, albumId) {
            var i, len;
            log('CSPhotoSelector - show Photos');

            // show loader until we get a response
            $jqloader.show();

            if (!$jqphotos || albumId) {
                return buildPhotoSelector(function () {
                    showPhotoSelector(callback);
                }, albumId);
            } else {
                // Update classnames to represent the selections for this instance
                $jqphotos.removeClass(settings.albumSelectedClass + ' ' + settings.albumDisabledClass + ' ' + settings.photoFilteredClass);
                for (i = 0, len = photos.length; i < len; i += 1) {
                    if ($jq.inArray(photos[i].id, selectedPhotoIds) !== -1) {
                        $jq($jqphotos[i]).addClass(settings.albumSelectedClass);
                    }
                    if ($jq.inArray(photos[i].id, disabledPhotoIds) !== -1) {
                        $jq($jqphotos[i]).addClass(settings.albumDisabledClass);
                    }
                }
                // Update paging
                $jqselectedCount.html(selectedPhotoIds.length);
                $jqselectedCountMax.html(instanceSettings.maxSelection);
                updatePhotosContainer(1);
                // updatePaginationButtons(1);
                $jqcontainer.fadeIn(100);
                if (typeof callback === 'function') {
                    callback();
                }
            }
        };

        hidePhotoSelector = function () {
            $jqphotosWrapper.removeClass('CSPhoto_container_active');
        };

        hideAlbumSelector = function () {
            unbindEvents();
            $jqcontainer.fadeOut(100);
            $jqcontainer.css('display','none');
        };

        placeLoadingScreen = function () {
            unbindEvents();
            $jqcontainer.fadeOut(100);
            $jqcontainerLoading.show();
        };

        getselectedAlbumIds = function () {
            return selectedAlbumIds;
        };

        getselectedPhotoIds = function () {
            return selectedPhotoIds;
        };

        /**
        * Disabled photos are greyed out in the interface and are not selectable.
        */
        setDisabledPhotoIds = function (input) {
            disabledPhotoIds = input;
        };

        /**
        * Remove selections, clear disabled list, go to page 1, etc
        */
        reset = function () {
            if (!albums || albums.length === 0) {
                return;
            }
            // hide the photo container
            $jqphotosWrapper.removeClass('CSPhoto_container_active');
            $jqbuttonOK.hide();
            $jqalbumsContainer.empty();
            $jqphotosContainer.empty();
            selectedAlbumIds = [];
            selectedPhotoIds = [];
            $jqalbums = null;
            $jqselectedCount.html("0");
            disabledPhotoIds = [];
            updatePaginationButtons(1);
        };

        /////////////////////////////////////////
        // PRIVATE FUNCTIONS FOR AN INSTANCE
        /////////////////////////////////////////

        // Add event listeners
        bindEvents = function () {
            $jqbuttonClose.bind('click', function (e) {
                e.preventDefault();
                hideAlbumSelector();
            });
            $jqbuttonCancel.bind('click', function (e) {
                e.preventDefault();
                hideAlbumSelector();
            });

            $jqbuttonOK.bind('click', function (e) {
                e.preventDefault();
                //hideAlbumSelector();
                placeLoadingScreen();
                if (typeof instanceSettings.callbackSubmit === "function") { instanceSettings.callbackSubmit(selectedPhotoIds); }
            });

            $jqbackToAlbums.bind('click', function (e) {
                e.preventDefault();
                $jqpagination.show();
                if (selectedPhotoIds.length == 0) {
                    $jqbuttonOK.hide();
                }
                hidePhotoSelector();
            });

            $jqpagePrev.bind('click', function (e) {
                var pageNumber = parseInt($jqpageNumber.text(), 10) - 1;
                e.preventDefault();
                if (pageNumber < 1) { return; }
                updateAlbumContainer(pageNumber);
                updatePaginationButtons(pageNumber);
            });

            $jqpageNext.bind('click', function (e) {
                var pageNumber = parseInt($jqpageNumber.text(), 10) + 1;
                e.preventDefault();
                if ($jq(this).hasClass(settings.disabledClass)) { return; }
                updateAlbumContainer(pageNumber);
                updatePaginationButtons(pageNumber);
            });

            $jq(window).bind('keydown', function (e) {
                if (e.which === 27) {
                    // The escape key has the same effect as the close button
                    e.preventDefault();
                    e.stopPropagation();
                    hideAlbumSelector();
                }
            });
        };

        // Remove event listeners
        unbindEvents = function () {
            $jqbuttonClose.unbind('click');
            $jqbuttonOK.unbind('click');
            $jqbuttonCancel.unbind('click');
            $jqalbumsContainer.children().unbind('click');
            $jqphotosContainer.children().unbind('click');
            $jqpagePrev.unbind('click');
            $jqpageNext.unbind('click');
            $jq(window).unbind('keydown');
        };

        // Set the contents of the albums container
        updateAlbumContainer = function (pageNumber) {
            var firstIndex, lastIndex;
            firstIndex = (pageNumber - 1) * instanceSettings.albumsPerPage;
            lastIndex = pageNumber * instanceSettings.albumsPerPage;
            $jqalbumsContainer.html($jqalbums.not("." + settings.photoFilteredClass).slice(firstIndex, lastIndex));
            $jqalbumsContainer.children().bind('click', function (e) {
                e.preventDefault();
                selectAlbum($jq(this));
            });
        };

        // Set the contents of the photos container
        updatePhotosContainer = function (pageNumber) {
            var firstIndex, lastIndex;
            firstIndex = (pageNumber - 1) * instanceSettings.photosPerPage;
            lastIndex = pageNumber * instanceSettings.photosPerPage;
            $jqphotosContainer.html($jqphotos.not("." + settings.photoFilteredClass).slice(firstIndex, lastIndex));
            $jqphotosContainer.children().bind('click', function (e) {
                e.preventDefault();
                selectPhotos($jq(this));
            });
        };

        updatePaginationButtons = function (pageNumber) {
            var numPages = Math.ceil((albums.length) / instanceSettings.albumsPerPage);
            $jqpageNumber.html(pageNumber);
            $jqpageNumberTotal.html(numPages);
            if (pageNumber === 1 || numPages === 1) {
                $jqpagePrev.addClass(settings.disabledClass);
            } else {
                $jqpagePrev.removeClass(settings.disabledClass);
            }
            if (pageNumber === numPages || numPages === 1) {
                $jqpageNext.addClass(settings.disabledClass);
            } else {
                $jqpageNext.removeClass(settings.disabledClass);
            }
        };

        selectAlbum = function ($jqalbum) {
            var albumId, i, len, removedId;
            albumId = $jqalbum.attr('data-id');

            // If the album is disabled, ignore this
            if ($jqalbum.hasClass(settings.albumDisabledClass)) {
                return;
            }

            if (!$jqalbum.hasClass(settings.albumSelectedClass)) {
                // If autoDeselection is enabled and they have already selected the max number of albums, deselect the first album
                if (instanceSettings.autoDeselection && selectedAlbumIds.length === instanceSettings.maxSelection) {
                    removedId = selectedAlbumIds.splice(0, 1);
                    $jqgetAlbumById(removedId).removeClass(settings.albumSelectedClass);
                    $jqselectedCount.html(selectedAlbumIds.length);
                }
                if (selectedAlbumIds.length < instanceSettings.maxSelection) {
                    // Add album to selectedAlbumIds
                    if ($jq.inArray(albumId, selectedAlbumIds) === -1) {
                        selectedAlbumIds.push(albumId);
                        $jqalbum.addClass(settings.albumSelectedClass);
                        $jqselectedCount.html(selectedAlbumIds.length);
                        log('CSPhotoSelector - newInstance - selectAlbum - selected IDs: ', selectedAlbumIds);
                        if (typeof instanceSettings.callbackAlbumSelected === "function") { instanceSettings.callbackAlbumSelected(albumId); }
                    } else {
                        log('CSPhotoSelector - newInstance - selectAlbum - ID already stored');
                    }
                }

            } else {
                // Remove album from selectedAlbumIds
                for (i = 0, len = selectedAlbumIds.length; i < len; i += 1) {
                    if (selectedAlbumIds[i] === albumId) {
                        selectedAlbumIds.splice(i, 1);
                        $jqalbum.removeClass(settings.albumSelectedClass);
                        $jqselectedCount.html(selectedAlbumIds.length);
                        if (typeof instanceSettings.callbackAlbumUnselected === "function") { instanceSettings.callbackAlbumUnselected(albumId); }
                        return false;
                    }
                }
            }

            if (selectedAlbumIds.length === instanceSettings.maxSelection) {
                if (typeof instanceSettings.callbackMaxSelection === "function") { instanceSettings.callbackMaxSelection(); }
            }
        };

        selectPhotos = function ($jqphoto) {
            var photoId, i, len, removedId;
            photoId = $jqphoto.attr('data-id');

            // If the photo is disabled, ignore this
            if ($jqphoto.hasClass(settings.albumDisabledClass)) {
                return;
            }

            if (!$jqphoto.hasClass(settings.albumSelectedClass)) {
                // If autoDeselection is enabled and they have already selected the max number of photos, deselect the first photo
                if (instanceSettings.autoDeselection && selectedPhotoIds.length === instanceSettings.maxSelection) {
                    removedId = selectedPhotoIds.splice(0, 1);
                    $jqgetPhotoById(removedId).removeClass(settings.albumSelectedClass);
                    $jqselectedCount.html(selectedPhotoIds.length);
                }
                if (selectedPhotoIds.length < instanceSettings.maxSelection) {
                    // Add photo to selectedPhotoIds
                    if ($jq.inArray(photoId, selectedPhotoIds) === -1) {
                        selectedPhotoIds.push(photoId);
                        $jqphoto.addClass(settings.albumSelectedClass);
                        $jqselectedCount.html(selectedPhotoIds.length);
                        log('CSPhotoSelector - newInstance - selectPhoto - selected IDs: ', selectedPhotoIds);
                        if (typeof instanceSettings.callbackPhotoSelected === "function") { instanceSettings.callbackPhotoSelected(photoId); }
                    } else {
                        log('CSPhotoSelector - newInstance - selectPhoto - ID already stored');
                    }
                }

            } else {
                // Remove photo from selectedPhotoIds
                for (i = 0, len = selectedPhotoIds.length; i < len; i += 1) {
                    if (selectedPhotoIds[i] === photoId) {
                        selectedPhotoIds.splice(i, 1);
                        $jqphoto.removeClass(settings.albumSelectedClass);
                        $jqselectedCount.html(selectedPhotoIds.length);
                        if (typeof instanceSettings.callbackPhotoUnselected === "function") { instanceSettings.callbackPhotoUnselected(photoId); }
                        return false;
                    }
                }
            }

            if (selectedPhotoIds.length === instanceSettings.maxSelection) {
                if (typeof instanceSettings.callbackMaxSelection === "function") { instanceSettings.callbackMaxSelection(); }
            }

            // log(selectedPhotoIds);
        };

        // Return an object with access to the public members
        return {
            showAlbumSelector: showAlbumSelector,
            showPhotoSelector: showPhotoSelector,
            hidePhotoSelector: hidePhotoSelector,
            hideAlbumSelector: hideAlbumSelector,
            getselectedAlbumIds: getselectedAlbumIds,
            getselectedPhotoIds: getselectedPhotoIds,
            setDisabledPhotoIds: setDisabledPhotoIds,
            reset: reset
        };
    };

    /////////////////////////////////////////
    // PRIVATE FUNCTIONS FOR GLOBAL PLUGIN
    /////////////////////////////////////////

    $jqgetAlbumById = function (id) {
        var i, len;
        id = id.toString();
        for (i = 0, len = albums.length; i < len; i += 1) {
            if (albums[i].id === id) {
                return $jq($jqalbums[i]);
            }
        }
        return $jq("");
    };

    $jqgetPhotoById = function (id) {
        var i, len;
        id = id.toString();
        for (i = 0, len = photos.length; i < len; i += 1) {
            if (photos[i].id === id) {
                return $jq($jqphotos[i]);
            }
        }
        return $jq("");
    };

    /**
    * Load the Facebook albums and build the markup
    */
    buildAlbumSelector = function (id, callback) {
        var buildMarkup, buildAlbumMarkup;
        log("buildAlbumSelector");
        $jqpagination.show();

        if (!FB) {
            log('The Facebook SDK must be initialised before showing the photo selector');
            return false;
        }

        // Check that the user is logged in to Facebook
        FB.getLoginStatus(function (response) {
            if (response.status === 'connected') {
                var accessToken = response.authResponse.accessToken;
                // Load Facebook photos
                FB.api('/' + id + '/albums', function (response) {
                    if (response.data.length) {
                        setAlbums(response.data);
                        // Build the markup
                        buildMarkup(accessToken);
                        // Call the callback
                        if (typeof callback === 'function') { callback(); }
                    } else {
                        $jqcontainerNoPhotos.show();
                        log('CSPhotoSelector - buildAlbumSelector - No albums returned');
                        return false;
                    }
                });
            } else {
                log('CSPhotoSelector - buildAlbumSelector - User is not logged in to Facebook');
                return false;
            }
        });

        // Build the markup of the album selector
        buildMarkup = function (accessToken) {
            // loop through photo albums
            var i, len, html = '';
            for (i = 0, len = albums.length; i < len; i += 1) {
                html += buildAlbumMarkup(albums[i], accessToken);
            }
            $jqalbums = $jq(html);
        };

        // Return the markup for a single album
        buildAlbumMarkup = function (album, accessToken) {
            return '<a href="#" class="CSPhotoSelector_album" data-id="' + album.id + '">' +
					'<div class="CSPhotoSelector_albumWrap"><div>' +
					'<img src="https://graph.facebook.com/' + album.id + '/picture?type=album&access_token=' + accessToken + '" alt="' + album.name + '" class="CSPhotoSelector_photoAvatar" />' +
					'</div></div>' +
					'<div class="CSPhotoSelector_photoName">' + album.name + '</div>' +
					'</a>';
            // return '<a href="#" class="CSPhotoSelector_photo CSPhotoSelector_clearfix" data-id="' + album.id + '">' +
            // 		'<img src="https://graph.facebook.com/'+ album.id +'/picture?type=album&access_token='+ accessToken +'" width="75" height="75" alt="' + album.name + '" class="CSPhotoSelector_photoAvatar" />' +
            // 		'<div class="CSPhotoSelector_photoName">' + 
            // 			'<span class="CSPhotoSelector_albumName">' + album.name + '</span>' +
            // 		'</div>' +
            // 		'</a>';
        };
    };

    /**
    * Load the Facebook photos and build the markup
    */
    buildPhotoSelector = function (callback, albumId) {
        var buildSecondMarkup, buildPhotoMarkup;
        log("buildPhotoSelector");

        FB.api('/' + albumId + '/photos?fields=id,picture,source,height,width&limit=500', function (response) {
            if (response.data) {
                setPhotos(response.data);
                // Build the markup
                buildSecondMarkup();
                // Call the callback
                if (typeof callback === 'function') {
                    callback();
                    // hide the loader and pagination
                    $jqloader.hide();
                    $jqpagination.hide();
                    // set the photo container to active
                    $jqphotosWrapper.addClass('CSPhoto_container_active');
                }
            } else {
                log('CSPhotoSelector - showPhotoSelector - No photos returned');
                return false;
            }
        });

        // Build the markup of the photo selector
        buildSecondMarkup = function () {
            //loop through photos
            var i, len, html = '';
            // if photos is empty, we need to try again

            if (!photos) {
                buildPhotoSelector(null, albumId);
            }

            if (photos.length > 0) {
                for (i = 0, len = photos.length; i < len; i += 1) {
                    html += buildPhotoMarkup(photos[i]);
                }
            } else {
                html = "<p>There are no photos to import from this album</p>";
            }

            $jqphotos = $jq(html);
        };

        buildPhotoMarkup = function (photo) {
            return '<a href="#" class="CSPhotoSelector_photo CSPhotoSelector_clearfix" data-id="' + photo.id + '">' +
					'<span><img src="' + photo.picture + '" alt="" class="CSPhotoSelector_photoAvatar" /></span>' +
					'</a>';
        };
    };


    sortPhotos = function (photo1, photo2) {
        if (photo1.upperCaseName === photo2.upperCaseName) { return 0; }
        if (photo1.upperCaseName > photo2.upperCaseName) { return 1; }
        if (photo1.upperCaseName < photo2.upperCaseName) { return -1; }
    };

    log = function () {
        if (settings && settings.debug && window.console) {
            console.log(Array.prototype.slice.call(arguments));
        }
    };

    module = {
        init: init,
        setAlbums: setAlbums,
        getAlbums: getAlbums,
        getAlbumById: getAlbumById,
        setPhotos: setPhotos,
        getPhotos: getPhotos,
        getPhotoById: getPhotoById,
        newInstance: newInstance
    };
    return module;

} (CSPhotoSelector || {}, jQuery));